package moodish.client;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.LinkedList;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

import moodish.comm.ClientComm;

public class GUI implements MoodishClient {
	private final String[] MOODS = { "Wonderful", "Loved", "Happy", "Good",
			"Accepted", "Creative", "Cheerful", "Determined", "Content",
			"At ease", "Challenged", "Careless", "Confused", "Clueless", "Shy",
			"Annoyed", "Bored", "Sleepy", "Lonely", "Tired", "Needy",
			"Uncomfortable", "Paranoid", "Hungry", "Disgruntled", "Cheated",
			"Frazzled", "Run-down" };

	private JFrame window = new JFrame("Moodish");
	private final String[] CONNECT_DISCONNECT_VALUES = { "Disconnect",
			"Connect" };
	private JButton connectDisconnect = new JButton(
			CONNECT_DISCONNECT_VALUES[1]), friend = new JButton("Friend"),
			unfriend = new JButton("Unfriend"), myMoods = new JButton(
					"My previous moods"), friendMoods = new JButton(
					"Friend's previous moods");
	private JComboBox<String> sendMood = new JComboBox<String>(MOODS);
	private JTabbedPane tabbedPane = new JTabbedPane();
	private ArrayList<MoodishFriend> friends = new ArrayList<MoodishFriend>();
	private ArrayList<String> connectedNicknames = new ArrayList<String>();
	private FriendsTableModel friendTableModel = new FriendsTableModel();
	private ConnectedTableModel connectedTableModel = new ConnectedTableModel();
	private JTable friendTable = new JTable(friendTableModel);
	private JTable connectedTable = new JTable(connectedTableModel);

	private String serverAddr = "", nickName = "";
	private int userCurrentMoodIndex = -1;
	private LinkedList<String> userMoods = new LinkedList<String>();

	private ClientComm clientComm;

	private MessageReader serverMessageInterpreter;
	private MoodsWindow userMoodsWindow;
	private MoodsWindow friendMoodsWindow;

	public GUI() {
		buildGUI();
		addListeners();

	}

	/**
	 * starts the GUI
	 */
	public void start(ClientComm clientComm) {
		this.clientComm = clientComm;
		window.setVisible(true);
	}

	/**
	 * Adds the appropriate Listeners for each component for the user to interact with
	 */
	private void addListeners() {
		//Window
		window.addWindowListener(new WindowListener() {
			
			@Override
			public void windowOpened(WindowEvent e) {}
			
			@Override
			public void windowIconified(WindowEvent e) {}
			
			@Override
			public void windowDeiconified(WindowEvent e) {}
			
			@Override
			public void windowDeactivated(WindowEvent e) {}
			
			@Override
			public void windowClosing(WindowEvent e) {
				if(clientComm.isConnected())
					clientComm.disconnect();
			}
			
			@Override
			public void windowClosed(WindowEvent e) {}
			
			@Override
			public void windowActivated(WindowEvent e) {}
		});
		
		// Top Buttons
		connectDisconnect.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				connectDisconnect.setEnabled(false);
				if (connectDisconnect.getText().equals(
						CONNECT_DISCONNECT_VALUES[1])) {
					serverAddr = JOptionPane.showInputDialog(window,
							"Please type the Moodish server address (localhost by default):",
							"Address", JOptionPane.INFORMATION_MESSAGE);
					if (serverAddr == null) {
						connectDisconnect.setEnabled(true);
						return;
					}
					else if(serverAddr.equals(""))
						serverAddr="localhost";
					nickName = JOptionPane.showInputDialog(window,
							"Please choose your nickname:", "nick",
							JOptionPane.INFORMATION_MESSAGE);
					if (nickName == null || nickName.trim().equals("")) {
						JOptionPane.showMessageDialog(window,
								"Invalid nickname.");
						connectDisconnect.setEnabled(true);
						return;
					}

					try {
						clientComm.connect(serverAddr, nickName);
						connectDisconnect.setText(CONNECT_DISCONNECT_VALUES[0]);
						serverMessageInterpreter = new MessageReader(GUI.this,
								clientComm);
						new Thread(serverMessageInterpreter).start();
						window.setTitle("Moodish - Connected as " + nickName);
					} catch (UnknownHostException e1) {
						JOptionPane.showMessageDialog(window, "Unknown host.");
					} catch (IOException e1) {
						JOptionPane.showMessageDialog(window,
								"Could not connect to server");
					} finally {
						connectDisconnect.setEnabled(true);
					}
				} else {
					clientComm.disconnect();
					connectionLost();
					connectDisconnect.setText(CONNECT_DISCONNECT_VALUES[1]);
					connectDisconnect.setEnabled(true);
				}
			}
		});

		sendMood.addItemListener(new ItemListener() {

			@Override
			public void itemStateChanged(ItemEvent e) {
				if (e.getStateChange() == ItemEvent.SELECTED) {
					if (clientComm.isConnected()) {
						if (sendMood.getSelectedIndex() != userCurrentMoodIndex) {
							int confirm = JOptionPane
									.showConfirmDialog(
											window,
											"Do you wish to change your mood to "
													+ MOODS[sendMood
															.getSelectedIndex()]
													+ "?","Change Mood", JOptionPane.YES_NO_OPTION);
							if (confirm == JOptionPane.OK_OPTION) {
								userCurrentMoodIndex = sendMood
										.getSelectedIndex();
								userMoods.addFirst(MOODS[userCurrentMoodIndex]);
								clientComm
										.sendMoodishMessage(MOODS[userCurrentMoodIndex]);
								if (userMoodsWindow != null)
									userMoodsWindow.update();
							} else
								sendMood.setSelectedIndex(userCurrentMoodIndex);
						}
					} else {
						JOptionPane.showMessageDialog(window,
								"Cannot change your mood unless connected");
						sendMood.setSelectedIndex(-1);
					}
				}

			}
		});

		myMoods.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if (userMoodsWindow != null)
					userMoodsWindow.getFrame().dispose();
				userMoodsWindow = new MoodsWindow(window, userMoods, "Your Moods");
			}
		});

		// Connected tab buttons
		friend.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if (clientComm.isConnected()) {
					if (connectedTable.getSelectedRow() >= 0) {
						MoodishFriend newFriend = new MoodishFriend();
						newFriend.name = connectedNicknames.get(connectedTable
								.getSelectedRow());
						boolean foundWithSameName = false;
						for (int i = 0; i < friends.size(); i++) {
							if (friends.get(i).name.equals(newFriend.name))
								foundWithSameName = true;
						}
						if (foundWithSameName)
							JOptionPane.showMessageDialog(window,
									"Already a friend");
						else {
							friends.add(newFriend);
							clientComm.friendship(newFriend.name);
							friendTableModel.fireTableDataChanged();
							JOptionPane
									.showMessageDialog(window,
											"You are now a friend of "
													+ newFriend.name);
						}

					} else {
						JOptionPane.showMessageDialog(window,
								"Please select a row");
					}
				} else {
					JOptionPane.showMessageDialog(window,
							"Please connect first");
				}
			}
		});

		// Friends tab buttons
		unfriend.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if (clientComm.isConnected()) {
					if (friendTable.getSelectedRow() >= 0) {
						clientComm.unfriendship(friends.get(friendTable
								.getSelectedRow()).name);
						friends.remove(friendTable.getSelectedRow());
						friendTableModel.fireTableDataChanged();
					} else
						JOptionPane.showMessageDialog(window,
								"Please select a row");
				} else {
					JOptionPane.showMessageDialog(window,
							"Please connect first");
				}
			}
		});

		friendMoods.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if (clientComm.isConnected()) {
					if (friendTable.getSelectedRow() >= 0) {
						if (friendMoodsWindow != null)
							friendMoodsWindow.getFrame().dispose();
						MoodishFriend friend = friends.get(friendTable
								.getSelectedRow());
						friendMoodsWindow = new MoodsWindow(window, friend.moods,
								friend.name + "'s Moods");
					} else
						JOptionPane.showMessageDialog(window,
								"Please select a row");
				} else {
					JOptionPane.showMessageDialog(window,
							"Please connect first");
				}

			}
		});
	}

	/**
	 * Builds the User Interface by putting each component where it belongs
	 */
	private void buildGUI() {
		// Building top buttons
		JPanel topButtons = new JPanel(new FlowLayout(FlowLayout.LEFT));
		topButtons.add(connectDisconnect);
		sendMood.setSelectedIndex(userCurrentMoodIndex);
		topButtons.add(sendMood);
		topButtons.add(myMoods);

		// Building connected tab
		JPanel connectedPanel = new JPanel(new BorderLayout());
		connectedPanel.setName("Connected");
		connectedPanel.add(new JScrollPane(connectedTable));
		JPanel connectedButtons = new JPanel();
		connectedButtons.add(friend);
		connectedPanel.add(connectedButtons, BorderLayout.SOUTH);
		tabbedPane.add(connectedPanel);

		// Building friends tab
		JPanel friendPanel = new JPanel(new BorderLayout());
		friendPanel.setName("Friends");
		friendPanel.add(new JScrollPane(friendTable));
		JPanel friendButtons = new JPanel();
		friendPanel.add(friendButtons, BorderLayout.SOUTH);
		friendButtons.add(unfriend);
		friendButtons.add(friendMoods);
		tabbedPane.add(friendPanel);

		// Finalizing window
		window.add(topButtons, BorderLayout.NORTH);
		window.add(tabbedPane);
		window.setMinimumSize(new Dimension(400, 300));
		window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	/**
	 * Represents a friend of the user
	 */
	private class MoodishFriend {
		String name = null;
		LinkedList<String> moods = new LinkedList<String>();
	}

	/**
	 * Organizes a table so it displays MoodishFriends
	 */
	private class FriendsTableModel extends AbstractTableModel {
		@Override
		public String getColumnName(int column) {
			switch (column) {
			case 0:
				return "Nickname";
			case 1:
				return "Mood";
			}
			return null;
		}

		@Override
		public int getColumnCount() {
			return 2;
		}

		@Override
		public int getRowCount() {
			return friends.size();
		}

		@Override
		public Object getValueAt(int row, int column) {
			switch (column) {
			case 0:
				return friends.get(row).name;
			case 1:
				if (friends.get(row).moods.size() > 0)
					return friends.get(row).moods.get(0);
				else
					return "";
			default:
				return null;
			}
		}

	}

	/**
	 * Organizes a table so it displays Strings representing the nicknames of
	 * the other users connected
	 */
	private class ConnectedTableModel extends AbstractTableModel {
		@Override
		public String getColumnName(int column) {
			return "Nickname";
		}

		@Override
		public int getColumnCount() {
			return 1;
		}

		@Override
		public int getRowCount() {
			return connectedNicknames.size();
		}

		@Override
		public Object getValueAt(int row, int column) {
			return connectedNicknames.get(row);
		}

	}

	/**
	 * Reset information related to the session. Call when connection to server
	 * is lost
	 */
	public void connectionLost() {
		window.setTitle("Moodish");
		connectDisconnect.setText("Connect");
		sendMood.setSelectedIndex(-1);
		friends = new ArrayList<MoodishFriend>();
		connectedNicknames = new ArrayList<String>();
		friendTableModel.fireTableDataChanged();
		connectedTableModel.fireTableDataChanged();
	}

	/**
	 * 
	 * @return nickname
	 */
	public String getUserNickName() {
		return nickName;
	}

	/**
	 * Adds the given nickname to the connected users
	 * 
	 * @param payLoad
	 */
	public void userConnected(String nickname) {
		if (nickname == null || nickname.equals(""))
			throw new IllegalArgumentException("Invalid nickname");
		connectedNicknames.add(nickname);
		connectedTableModel.fireTableDataChanged();
	}

	/**
	 * Removes the user with the given nickname from friends and from connected
	 * 
	 * @param nickname
	 */
	public void userDisconnected(String nickname) {
		for (MoodishFriend f : friends) {
			if (f.name.equals(nickname)) {
				friends.remove(f);
				friendTableModel.fireTableDataChanged();
				break;
			}
		}
		for (String c : connectedNicknames) {
			if (c.equals(nickname)) {
				connectedNicknames.remove(c);
				connectedTableModel.fireTableDataChanged();
				break;
			}
		}
	}

	/**
	 * Changes the mood of a friend, if existent
	 * 
	 * @param nickName
	 * @param mood
	 */
	public void setMood(String nickName, String mood) {
		for (int i = 0; i < friends.size(); i++)
			if (friends.get(i).name.equals(nickName)) {
				friends.get(i).moods.addFirst(mood);
				friendTableModel.fireTableDataChanged();
				break;
			}
		if (friendMoodsWindow != null)
			friendMoodsWindow.update();
	}

	/**
	 * 
	 * @return JFrame
	 */
	public JFrame getJFrame() {
		return window;
	}

}